//
// Created on 2025/7/18.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef STREAMCORE_BUFFER_H
#define STREAMCORE_BUFFER_H
#include <napi/native_api.h>
#include <stdexcept>
constexpr auto ERR_TYPE_CHECK_FAILED = "NAPI type check failed";
constexpr auto ERR_INVALID_BUFFER = "Invalid buffer type provided";
constexpr auto ERR_UNSUPPORTED_TYPE = "Unsupported TypedArray type , only Uint8Array is supported";
constexpr auto ERR_BUFFER_INFO_FAILED = "Failed to get buffer information";

#define NAPI_CHECK(call, err_msg)                                                                                      \
    do {                                                                                                               \
        if ((call) != napi_ok) {                                                                                       \
            throw std::runtime_error(err_msg);                                                                         \
        }                                                                                                              \
    } while (0)

static bool TryUint8Array(napi_env env, napi_value value, void **buf, size_t *len) {
    bool is_typedarray = false;
    NAPI_CHECK(napi_is_typedarray(env, value, &is_typedarray), ERR_TYPE_CHECK_FAILED);

    if (!is_typedarray)
        return false;

    napi_typedarray_type array_type;
    size_t length;
    void *data;
    napi_value arraybuffer;
    size_t byte_offset;

    NAPI_CHECK(napi_get_typedarray_info(env, value, &array_type, &length, &data, &arraybuffer, &byte_offset),
               ERR_BUFFER_INFO_FAILED);

    if (array_type != napi_uint8_array) {
        throw std::runtime_error(ERR_UNSUPPORTED_TYPE);
    }
    *buf = static_cast<uint8_t *>(data) + byte_offset;
    *len = length;
    return true;
}
static bool TryArrayBuffer(napi_env env, napi_value value, void **buf, size_t *len) {
    bool is_arraybuffer = false;
    NAPI_CHECK(napi_is_arraybuffer(env, value, &is_arraybuffer), ERR_TYPE_CHECK_FAILED);

    if (!is_arraybuffer)
        return false;

    NAPI_CHECK(napi_get_arraybuffer_info(env, value, buf, len), ERR_BUFFER_INFO_FAILED);
    return true;
}
static bool TryOHOSBuffer(napi_env env, napi_value value, void **buf, size_t *len) {
    bool is_buffer = false;
    NAPI_CHECK(napi_is_buffer(env, value, &is_buffer), ERR_TYPE_CHECK_FAILED);

    if (!is_buffer)
        return false;
    NAPI_CHECK(napi_get_buffer_info(env, value, buf, len), ERR_BUFFER_INFO_FAILED);
    return true;
}


static void GetBuffer(napi_env env, napi_value value, void **buf, size_t *len) {
    napi_valuetype type;
    NAPI_CHECK(napi_typeof(env, value, &type), ERR_TYPE_CHECK_FAILED);

    // 处理 undefined 情况
    if (type == napi_undefined) {
        throw std::runtime_error(ERR_INVALID_BUFFER);
    }

    // 按优先级检查支持的缓冲区类型
    if (TryUint8Array(env, value, buf, len))
        return;
    if (TryArrayBuffer(env, value, buf, len))
        return;
    if (TryOHOSBuffer(env, value, buf, len))
        return;

    // 所有尝试都失败
    throw std::runtime_error(ERR_INVALID_BUFFER);
}

class NapiBuffer {
public:
    static constexpr auto ERR_NAPI_REFERENCE_FAILED = "Failed to create reference for buffer";
    NapiBuffer():env_(nullptr), data_(nullptr), size_(0), ref_(nullptr){}
    NapiBuffer(napi_env env, napi_value value) : env_(env), data_(nullptr), size_(0), ref_(nullptr) {
        get(env, value);
    }

    ~NapiBuffer() { release(); }

    // 禁止拷贝
    NapiBuffer(const NapiBuffer &) = delete;
    NapiBuffer &operator=(const NapiBuffer &) = delete;

    NapiBuffer(NapiBuffer &&other) noexcept
        : env_(other.env_), data_(other.data_), size_(other.size_), ref_(other.ref_) {
        other.data_ = nullptr;
        other.size_ = 0;
        other.env_ = nullptr;
        other.ref_ = nullptr;
    }

    NapiBuffer &operator=(NapiBuffer &&other) noexcept {
        if (this != &other) {
            // 释放当前资源
            release();

            // 转移资源
            env_ = other.env_;
            data_ = other.data_;
            size_ = other.size_;
            ref_ = other.ref_;

            // 置空原对象
            other.env_ = nullptr;
            other.data_ = nullptr;
            other.size_ = 0;
            other.ref_ = nullptr;
        }
        return *this;
    }
    
    void get(napi_env env, napi_value value) {
         GetBuffer(env, value, &data_, &size_);
        // 在buffer释放前保持引用
        NAPI_CHECK(napi_create_reference(env, value, 1, &ref_), ERR_NAPI_REFERENCE_FAILED);
    }

    void *data() const noexcept { return data_; }
    size_t size() const noexcept { return size_; }

    bool empty() const noexcept { return size_ == 0; }

    template <typename T = uint8_t> T *typed_data() const {
        static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
        return static_cast<T *>(data_);
    }

    uint8_t *begin() const noexcept { return static_cast<uint8_t *>(data_); }
    uint8_t *end() const noexcept { return static_cast<uint8_t *>(data_) + size_; }

    explicit operator bool() const noexcept { return data_ != nullptr && size_ > 0; }

    void sub(uint32_t offset, size_t len) {
        data_ = static_cast<uint8_t *>(data_) + offset;
        size_ = len;
    }

private:
    void release() noexcept {
        if (env_ != nullptr && ref_ != nullptr) {
            napi_delete_reference(env_, ref_);
            env_ = nullptr;
            ref_ = nullptr;
        }
        data_ = nullptr;
        size_ = 0;
    }

    napi_env env_;
    napi_ref ref_;
    void *data_;
    size_t size_;
};

#endif // STREAMCORE_BUFFER_H
